/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package practica1.logic;

import java.io.File;
import java.util.ArrayList;
import practica1.domain.Experimento;
import practica1.domain.event.ExperimentoEvent;
import practica1.domain.listener.ExperimentoListener;
import practica1.logic.event.LogicExperimentoEvent;
import practica1.logic.event.LogicPoblacionEvent;
import practica1.logic.listener.LogicExperimentoListener;
import practica1.logic.listener.LogicPoblacionListener;
import practica1.util.HashCodeUtil;

/**
 * Clase que extiende del modelo de un experimento e implementa su parte lógica
 * @author Miguel González - Ceura
 */
public class LogicExperimento extends Experimento implements LogicPoblacionListener,
        ExperimentoListener {
    //Listeners
    private ArrayList<LogicExperimentoListener> listeners;
    
    //Contiene las poblaciones abiertas
    private ArrayList<LogicPoblacion> poblacionesAbiertas;
    
    //Contiene todas las poblaciones
    private ArrayList<LogicPoblacion> poblaciones;
    
    //Booleano, verdad si el experimento ha sido modificado, falso sino
    private boolean modified;
    
    /**
     * Constructor de un experimento lógico
     * @param fichExperimento Fichero del experimento
     * @param nombreExperimento Nombre del experimento
     * @param tipoBacteria Tipo de bacteria
     * @param nombreInvestigador Nombre del investigador
     * @param proyectoInvestigacion Proyecto de investigación
     */
    public LogicExperimento(File fichExperimento, String nombreExperimento,
            String tipoBacteria, String nombreInvestigador,
            String proyectoInvestigacion) {
        super(fichExperimento, nombreExperimento,
                tipoBacteria, nombreInvestigador, proyectoInvestigacion);
        
        poblacionesAbiertas = new ArrayList<LogicPoblacion>();
        poblaciones = new ArrayList<LogicPoblacion>();
        
        listeners = new ArrayList<LogicExperimentoListener>();
        modified = false;
        super.addExperimentoListener(this);
    }
    
    /**
    * Permite suscribirse a los cambios del modelo lógico del experimento
    * @param l LogicExperimentoListener
    */
    public void addLogicExperimentoListener(LogicExperimentoListener l) {
        if(l != null) {
            listeners.add(l);
        }
    }
    
    /**
     * Permite desuscribirse a los cambios del modelo lógico del experimento
     * @param l LogicExperimentoListener
     */
    public void removeLogicExperimentoListener(LogicExperimentoListener l) {
        if(l != null) {
            listeners.remove(l);
        }
    }

    /**
     * Devuelve si el experimento ha sido modificado o no
     * @return Devuelve verdad si ha sido modificado, sino falso
     */
    public boolean isModified() {
        return modified;
    }
    
    /**
     * Establece el estado del experimento
     * @param modified Verdad si ha sido modificado, falso sino
     */
    public void setModified(boolean modified) {
        this.modified = modified;
    }
    
    /**
     * Devuelve el número de poblaciones lógicas que contiene el experimento
     * @return int numPoblaciones
     */
    public int countPoblaciones() {
        return poblaciones.size();
    }
    
    /**
     * Devuelve el nombre del experimento
     * @return String nombreExperimento
     */
    @Override
    public String toString() {
        return getNombreExperimento();
    }
    
    /**
     * Compara un experimento lógico
     * @param experimento Object LogicExperimento
     * @return Devuelve verdad si son iguales, sino falso
     */
    @Override
    public boolean equals(Object experimento) {
        boolean expValido;
        
        if(experimento != null) {
            if(experimento instanceof Experimento) {
                Experimento ex = (Experimento) experimento;
                
                if(ex.getFichExperimento().equals(getFichExperimento()) &&
                        ex.getNombreExperimento().equals(getNombreExperimento())
                        && ex.getTipoBacteria().equals(getTipoBacteria()) &&
                        ex.getNombreInvestigador().equals(
                        getNombreInvestigador()) && ex.getProyectoInvestigacion()
                        .equals(getProyectoInvestigacion())) {
                    expValido = true;
                } else {
                    expValido = false;
                }
            } else {
                    expValido = false;
                }
        } else {
            expValido = false;
        }
        
        
        if(expValido && experimento != null) {
            if(experimento instanceof LogicExperimento) {
                LogicExperimento ex = (LogicExperimento) experimento;
                
                //Comprobamos si son iguales todas sus poblaciones
                ArrayList<LogicPoblacion> pob = ex.getPoblaciones();
                if(poblaciones.size() == pob.size()) {
                    for(int i=0; i<poblaciones.size(); i++) {
                        //Si no son iguales devolvemos false
                        if(!poblaciones.get(i).equals(pob.get(i))) {
                            return false;
                        }
                    }
                }
                    
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Devuelve el hashCode del experimento lógico
     * @return int hashCode
     */
    @Override
    public int hashCode() {
        int result = HashCodeUtil.SEED;
        
        result = HashCodeUtil.hash(result, super.hashCode());
        
        for(LogicPoblacion p : poblaciones) {
            result = HashCodeUtil.hash(result, p);    
        }
        
        return result;
    }
    
    /**
     * Añade una población lógica
     * @param poblacion LogicPoblacion
     */
    public void addPoblacion(LogicPoblacion poblacion) {
        poblaciones.add(poblacion);
        modified = true;
    }
    
    /**
     * Borra una población lógica
     * @param poblacion LogicPoblacion
     */
    public void removePoblacion(LogicPoblacion poblacion) {
        poblaciones.remove(poblacion);
        modified = true;
    }
    
    /**
     * Abre una población lógica, es decir, notifica para que sea mostrada en pantalla
     * @param poblacionAbrir LogicPoblacion
     */
    public void abrirPoblacion(LogicPoblacion poblacionAbrir) {
        int indexPoblacion = poblacionesAbiertas.indexOf(poblacionAbrir);
        //Si no está abierta
        if(indexPoblacion< 0) {
            poblacionesAbiertas.add(poblacionAbrir);
        
            poblacionAbrir.addLogicPoblacionListener(this);
            
            //Notificamos que hemos abierto una población
            for(LogicExperimentoListener l : listeners) {
                l.abiertaPoblacion(new LogicExperimentoEvent(poblacionAbrir));
            }
        } else {
            //Notificamos que se va a mostrar la población
            for(LogicExperimentoListener l : listeners) {
                l.mostrarPoblacion(new LogicExperimentoEvent(poblacionAbrir));
            }
        }
    }
    
    /**
     * Comprueba si una población se está mostrando en pantalla
     * @param poblacion LogicPoblacion
     * @return Devuelve verdad si se está mostrando, sino falso
     */
    public boolean isPoblacionAbierta(LogicPoblacion poblacion) {
        return poblacionesAbiertas.indexOf(poblacion) >= 0;
    }
    
    /**
     * Notifica de que una población debe de dejar de verse en pantalla
     * @param poblacionCerrar LogicPoblacion
     */
    public void cerrarPoblacion(LogicPoblacion poblacionCerrar) {
        int indexPoblacion = poblacionesAbiertas.indexOf(poblacionCerrar);
        //Si está abierta
        if(indexPoblacion >= 0) {
            poblacionesAbiertas.remove(indexPoblacion);

            //Notificamos que hemos cerrado una población
            for(LogicExperimentoListener l : listeners) {
                l.cerradaPoblacion(new LogicExperimentoEvent(poblacionCerrar));
            }
        }
    }
    
    /**
     * Notifica de que todas las poblaciones deben de dejar de verse
     */
    public void cerrarTodasPoblaciones() {
        while(poblacionesAbiertas.size() > 0) {
            LogicPoblacion p = poblacionesAbiertas.get(0);
            poblacionesAbiertas.remove(p);
            
            //Notificamos que hemos cerrado una población
            for(LogicExperimentoListener l : listeners) {
                l.cerradaPoblacion(new LogicExperimentoEvent(p));
            }
        }
    }

    /**
     * Método que es notificado cuando una población cambia y a su vez se encarga
     * de enviar la notificación a través del LogicExperimentoListener
     * @param event LogicPoblacionEvent
     */
    @Override
    public void poblacionChange(LogicPoblacionEvent event) {
        //Notificamos que el experimento ha sido modificado
        for(LogicExperimentoListener l : listeners) {
            l.poblacionChanged(new LogicExperimentoEvent(
                    event.getLogicPoblacion()));
        }
    }

    /**
     * Devuelve todas las poblaciones lógicas del experimento
     * @return ArrayList<LogicPoblacion>
     */
    public ArrayList<LogicPoblacion> getPoblaciones() {
        return poblaciones;
    }
    
    /**
     * Muestra una población que ya se encuentra abierta
     * @param poblacion LogicPoblacion
     */
    public void mostrarPoblacion(LogicPoblacion poblacion) {
        //Notificamos que queremos mostrar una población
        for(LogicExperimentoListener l : listeners) {
            l.mostrarPoblacion(new LogicExperimentoEvent(poblacion));
        }
    }

    /**
     * Método que se llama cuando se modifica el experimento
     * @param event ExperimentoEvent
     */
    @Override
    public void modificadoExperimento(ExperimentoEvent event) {
        modified = true;
    }
}
